﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace SudokuGame
{
    public partial class SudokuPanel : UserControl
    {
        #region 【字段】
        /// <summary>
        /// 资源管理器。
        /// </summary>
        ComponentResourceManager resources = new ComponentResourceManager(typeof(SudokuPanel));

        /// <summary>
        /// 数独项目集合。
        /// </summary>
        private SudokuCell[,] sudokuCellArray = new SudokuCell[9, 9];

        private Sudoku sudoku = null;
        #endregion

        #region 【属性】
        private SudokuCell selected;
        /// <summary>
        /// 获取当前被选中的数独项目。
        /// </summary>
        public SudokuCell Selected
        {
            get { return selected; }
            private set
            {
                if (selected == value || (value != null && value.ReadOnly))
                { return; }
                if (selected != null)
                {
                    this.Invalidate(Rectangle.Truncate(CellRectangleFArray[this.selected.I, this.selected.J]));
                }
                selected = value;
                if (selected != null)
                {
                    this.Invalidate(Rectangle.Truncate(CellRectangleFArray[this.selected.I, this.selected.J]));
                }
            }
        }

        /// <summary>
        /// 数独矩形框集合。
        /// </summary>
        public RectangleF[,] CellRectangleFArray
        {
            get { return this.GetCellRectangleFArray(); }
        }
        #endregion

        #region 【构造函数】
        /// <summary>
        /// 实例化一个数独面板对象。
        /// </summary>
        public SudokuPanel()
        {
            InitializeComponent();
        }
        #endregion

        #region 【公共方法】
        /// <summary>
        /// 开始游戏。
        /// </summary>
        public void Replay()
        {
            this.Replay(Level.Level1);
        }
        /// <summary>
        /// 开始游戏。
        /// </summary>
        /// <param name="level">游戏级别。</param>
        private void Replay(Level level)
        {
            this.sudoku = new Sudoku(level);
            this.Selected = null;
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    if (this.sudoku.SudokuTemplate[i, j] == 0)
                    {
                        sudokuCellArray[i, j].ReadOnly = false;
                        sudokuCellArray[i, j].Value = 0;
                        sudokuCellArray[i, j].ReadOnly = false;
                    }
                    else
                    {
                        sudokuCellArray[i, j].ReadOnly = false;
                        sudokuCellArray[i, j].Value = this.sudoku.SudokuTemplate[i, j];
                        sudokuCellArray[i, j].ReadOnly = true;
                    }
                }
            }
            this.Invalidate();
        }

        /// <summary>
        /// 提交结果。
        /// </summary>
        public void Commit()
        {
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    if (sudokuCellArray[i, j].Value == 0)//未完成填空
                    {
                        sudokuCellArray[i, j].ReadOnly = false;
                        this.Selected = sudokuCellArray[i, j];
                        return;
                    }
                }
            }

            if (this.sudoku == null) return;
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    this.sudoku.SudokuResult[i, j] = sudokuCellArray[i, j].Value;
                }
            }

            if (this.sudoku.CheckSudokuResult())
            {
                MessageBox.Show("Success");
            }
            else
            {
                MessageBox.Show("Failue");
            }

        }
        #endregion

        #region 【重载 OnLoad 方法】
        /// <summary>
        /// 引发 Load 事件。
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            this.InitializeSudokuCellArray();
        }

        /// <summary>
        /// 初始化数独项目集合。
        /// </summary>
        private void InitializeSudokuCellArray()
        {
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    sudokuCellArray[i, j] = new SudokuCell(i, j, 0, true);
                }
            }
        }
        #endregion

        #region 【重载 ProcessCmdKey 方法】
        /// <summary>
        /// 重载 ProcessCmdKey 方法
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="keyData"></param>
        /// <returns></returns>
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Up:
                    this.MoveSelected(-1, 0);
                    return true;
                case Keys.Down:
                    this.MoveSelected(1, 0);
                    return true;
                case Keys.Left:
                    this.MoveSelected(0, -1);
                    return true;
                case Keys.Right:
                    this.MoveSelected(0, 1);
                    return true;
            }
            return base.ProcessCmdKey(ref msg, keyData);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="vertical"></param>
        /// <param name="horizon"></param>
        private void MoveSelected(int vertical, int horizon)
        {
            int i = this.Selected.I;
            int j = this.Selected.J;
            do
            {
                int m = (i + horizon) >= 0 ? (i + horizon) / 9 : -1;
                int n = (j + vertical) >= 0 ? (j + vertical) / 9 : -1;
                i = (i + horizon + n + 9) % 9;
                j = (j + vertical + m + 9) % 9;
            } while (sudokuCellArray[i, j].ReadOnly && !(i == this.Selected.I && j == this.Selected.J));
            this.Selected = sudokuCellArray[i, j];
        }
        #endregion

        #region 【重载 OnKeyPress 方法】
        /// <summary>
        /// 引发 KeyPress 事件。
        /// </summary>
        /// <param name="e"></param>
        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            base.OnKeyPress(e);
            if (char.IsDigit(e.KeyChar))
            {
                int value = Convert.ToInt32(e.KeyChar.ToString());
                if (this.Selected != null && this.Selected.Value != value)
                {
                    this.Selected.Value = Convert.ToInt32(e.KeyChar.ToString());
                    this.Invalidate(Rectangle.Truncate(CellRectangleFArray[this.Selected.I, this.Selected.J]));
                }
            }
        }
        #endregion

        #region 【重载 OnMouseClick 方法】
        /// <summary>
        /// 引发 MouseClick 事件。
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            if (e.Button == MouseButtons.Left)
            {
                RectangleF[,] cellArray = this.GetCellRectangleFArray();
                for (int i = 0; i < 9; i++)
                {
                    for (int j = 0; j < 9; j++)
                    {
                        if (cellArray[i, j].Contains(e.Location))
                        {
                            this.Selected = sudokuCellArray[i, j];
                        }
                    }
                }
            }
        }
        #endregion

        #region 【重载 OnPaint 方法】
        /// <summary>
        /// 引发 Paint 事件。
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;
            RectangleF[,] cellArray = this.GetCellRectangleFArray();

            this.FillCellBackGround(g, cellArray);//填充方格背景。
            this.DrawCellLine(g);
            this.DrawBorder(g);

            this.DrawSudokuCell(g, cellArray);

            this.DrawSelectedCell(g, cellArray);

        }

        private void DrawSelectedCell(Graphics g, RectangleF[,] cellArray)
        {
            if (this.Selected != null)
            {
                GraphicsPath path = GetRoundRect(cellArray[this.Selected.I, this.Selected.J], 5f);
                Brush brush = new LinearGradientBrush(cellArray[this.Selected.I, this.Selected.J],
                    Color.FromArgb(10, 0, 0, 255),
                    Color.FromArgb(100, 0, 0, 255), LinearGradientMode.Vertical);
                g.FillPath(brush, path);
            }
        }
        #region 【绘制数独面板 】

        #region -- Public static methods --
        /// <summary>
        /// Gets a path of a rectangle with round corners.
        /// </summary>
        /// <param name="rect">The rectangle</param>
        /// <param name="radius">The corner radius</param>
        /// <returns></returns>
        public static GraphicsPath GetRoundRect(RectangleF rect, float radius)
        {
            var gp = new GraphicsPath();
            var diameter = radius * 2;
            gp.AddArc(rect.X + rect.Width - diameter, rect.Y, diameter, diameter, 270, 90);
            gp.AddArc(rect.X + rect.Width - diameter, rect.Y + rect.Height - diameter, diameter, diameter, 0, 90);
            gp.AddArc(rect.X, rect.Y + rect.Height - diameter, diameter, diameter, 90, 90);
            gp.AddArc(rect.X, rect.Y, diameter, diameter, 180, 90);
            gp.CloseFigure();
            return gp;
        }
        #endregion

        private void DrawSudokuCell(Graphics g, RectangleF[,] cellArray)
        {
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    if (sudokuCellArray[i, j] == null)
                    {
                        continue;
                    }
                    Image num = (Image)resources.GetObject("$number");
                    RectangleF srcRect = new RectangleF((sudokuCellArray[i, j].Value - 1) * (num.Width / 9f), 0, num.Width / 9f, num.Height);
                    g.DrawImage(num, cellArray[i, j], srcRect, GraphicsUnit.Pixel);
                }
            }
        }

        /// <summary>
        /// 填充所有方格的背景。
        /// </summary>
        /// <param name="g"></param>
        /// <param name="cellArray"></param>
        private void FillCellBackGround(Graphics g, RectangleF[,] cellArray)
        {
            using (Brush evenBrush = new SolidBrush(Color.FromArgb(255, 255, 217)), 
                oddBrush = new SolidBrush(Color.FromArgb(255, 255, 243)))
            {
                for (int i = 0; i < 9; i++)
                {
                    for (int j = 0; j < 9; j++)
                    {
                        if ((i / 3 + j / 3) % 2 == 0)
                        {
                            g.FillRectangle(evenBrush, cellArray[i, j]);
                        }
                        else
                        {
                            g.FillRectangle(oddBrush, cellArray[i, j]);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 获取所有方格的 RectangleF 集合。
        /// </summary>
        /// <returns></returns>
        private RectangleF[,] GetCellRectangleFArray()
        {
            RectangleF[,] cellArray = new RectangleF[9, 9];
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    float cellWidth = this.Width / 9;//每个格子的宽度。
                    float cellHeight = this.Height / 9;//每个格子的高度。
                    cellArray[i, j] = new RectangleF(new PointF(i * cellWidth, j * cellHeight), 
                        new SizeF(i == 8 ? this.Width - i * cellWidth : cellWidth,
                            j == 8 ? this.Height - j * cellHeight : cellHeight));
                }
            }
            return cellArray;
        }

        /// <summary>
        /// 绘制格子的边框线。
        /// </summary>
        /// <param name="g"></param>
        private void DrawCellLine(Graphics g)
        {
            float cellWidth = this.Width / 9;//每个格子的宽度。
            float cellHeight = this.Height / 9;//每个格子的高度。
            using (Pen cellPen = new Pen(Color.FromArgb(180, 179, 177)))
            {
                for (int i = 1; i < 9; i++)
                {
                    g.DrawLine(cellPen, new PointF(0, i * cellHeight), new PointF(this.Width, i * cellHeight));
                    g.DrawLine(cellPen, new PointF(i * cellWidth, 0), new PointF(i * cellWidth, this.Height));
                }
            }
        }

        /// <summary>
        /// 绘制边框。
        /// </summary>
        /// <param name="g"></param>
        private void DrawBorder(Graphics g)
        {
            using (Pen borderPen = new Pen(Brushes.Black, 4f))
            {
                g.DrawRectangle(borderPen, this.ClientRectangle);
            }
        }

        #endregion
        #endregion

        #region 【Windows 窗体设计器生成的代码】
        /// <summary>
        /// 设计器支持所需的代码 - 不要
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SudokuPanel));
            this.SuspendLayout();
            // 
            // SudokuPanel
            // 
            this.Name = "SudokuPanel";
            this.Size = new System.Drawing.Size(167, 177);
            this.ResumeLayout(false);

        }
        #endregion
    }
}
